#include "../metric/xyz.h"
#include "mTree.h"
#include "mTreeNode.h"
#include <assert.h>

int split(struct mTree * tree, off_t node,   struct mTreeNode * node_ptr, off_t object, void * object_ptr);
double get_distance(const struct mTree * tree, void * query_object_ptr, const off_t tree_object);
void mTreeNode_updateRadius(struct mTree * tree, off_t node, struct mTreeNode * node_ptr, double distanceToNode);
void calculate_paird(off_t objects[MTREE_BFACTOR+1], off_t child[MTREE_BFACTOR+1], double paird[MTREE_BFACTOR+1][MTREE_BFACTOR+1], double radii[MTREE_BFACTOR+1],
                     struct mTree * tree, struct mTreeNode * node_ptr,
                     off_t object, void * object_ptr);




struct mTree* open_tree();
void close_tree(struct mTree* tree);
void test_getDistance(struct mTree * tree);
void test_updateRadius(struct mTree * tree);
void test_calculate_paird_leaf(struct mTree * tree);
void test_calculate_paird_node(struct mTree * tree);

int main () {
  struct mTree * tree;

  tree = open_tree();
  test_getDistance(tree);
  close_tree(tree);

  tree = open_tree();
  test_updateRadius(tree);
  close_tree(tree);

  tree = open_tree();
  test_calculate_paird_node(tree);
  close_tree(tree);
    
  tree = open_tree();
  test_calculate_paird_leaf(tree);
  close_tree(tree);
    

  return 0;

}

struct mTree* open_tree() {
  struct store * store;
  store = store_open_cached("t", "t.fat", sizeof(struct xyz_int), 100, 1000);
  return mTree_open_cached(store, "t.mTree", "t.mTree.fat", 1000, (double(*)(const void*,const void*))&xyz_int_distance);

}

void close_tree(struct mTree * tree) {
  struct store * store = tree->oStore;
  mTree_close(tree);
  store_close(store);
}

void test_getDistance(struct mTree* tree) {
  off_t o1 = store_new(tree->oStore);
  off_t o2 = store_new(tree->oStore);

  struct xyz_int* o1_ptr = store_read(tree->oStore, o1);
  struct xyz_int* o2_ptr = store_read(tree->oStore, o2);

  double distance1, distance2;

  
  o1_ptr->x = 0;
  o1_ptr->y = 0;
  o1_ptr->z = 0;
  //  o1_ptr->i = 0;

  o2_ptr->x = 300;
  o2_ptr->y = 400;
  o2_ptr->z = 0;
  //  o2_ptr->i = 1;
  
  store_write(tree->oStore, o1);
  store_write(tree->oStore, o2);

  distance1 = xyz_int_distance(o1_ptr, o2_ptr);
  distance2 = get_distance(tree, o2_ptr, o1);
  
  if(distance1 == distance2) 
    printf("PASS: getDistance1 %f = %f\n", distance1, distance2);
  else 
    printf("FAIL: getDistance2 %f != %f\n", distance1, distance2);
  
  store_release(tree->oStore, o1);
  store_release(tree->oStore, o2);


}

void test_updateRadius(struct mTree * tree) {
  off_t n1 = store_new(tree->nStore);
  struct mTreeNode * n1_ptr = store_read(tree->nStore, n1);
  int fail = 0;
  n1_ptr->radius = 0;
  
  mTreeNode_updateRadius(tree, n1, n1_ptr, 1.0);

  if(n1_ptr->radius != 1.0) {
    fail = 1;
  }

  mTreeNode_updateRadius(tree, n1, n1_ptr, 0.0);
 
  if(n1_ptr->radius != 1.0) { 
    fail += 2;
  }

  if(fail) {
    printf ("FAIL: updateRadius, code = %d\n", fail);
  } else {
    printf ("PASS: updateRadius\n");
  }

  store_release(tree->nStore, n1);

}

void test_calculate_paird_leaf(struct mTree * tree) {
  off_t objects [MTREE_BFACTOR+1];
  off_t child [MTREE_BFACTOR+1];
  double paird[MTREE_BFACTOR+1][MTREE_BFACTOR+1];
  double radii[MTREE_BFACTOR+1];
  struct mTreeNode * node_ptr;
  off_t object;

  struct xyz_int * object_ptr;
  int i;

  off_t node;

  node = store_new (tree->nStore);
  node_ptr = store_read(tree->nStore, node);
  node_ptr->isLeaf = 1;

  for(i = 0; i < MTREE_BFACTOR; i++) {
    struct xyz_int * this_object_ptr;
    node_ptr->children[i] = store_new(tree->oStore);
    this_object_ptr = store_read(tree->oStore, node_ptr->children[i]);

    this_object_ptr->x = i;
    this_object_ptr->y = 0;
    this_object_ptr->z = 0;
    //    this_object_ptr->i = i;

    store_write(tree->oStore, node_ptr->children[i]);
    store_release(tree->oStore, node_ptr->children[i]);

  }

  store_write(tree->nStore, node);

  object = store_new(tree->oStore);
  object_ptr = store_read(tree->oStore, object);
  object_ptr->x = i;
  object_ptr->y = 0;
  object_ptr->z = 0;
  //  object_ptr->i = i;
  
  calculate_paird(objects, child, paird, radii, tree, node_ptr, object, object_ptr);

  for(i = 0; i < MTREE_BFACTOR+1; i++) {
    int j;
    assert(radii[i] == 0);
    if(i < MTREE_BFACTOR) {
      
      assert(objects[i] == node_ptr->children[i]);
    } else {

      assert(objects[i] == object);
    }
    assert(child[i] == objects[i]);

    for(j = 0; j < MTREE_BFACTOR+1; j++) {
      if(i < j) {
	assert(paird[i][j] == (double)(j-i));
      } else {
	assert(paird[i][j] == (double)(i-j));
      }
    }
  }

  store_release(tree->oStore, object);
  store_release(tree->nStore, node);

  printf("PASS: calculate_paird (leaf)\n");



}

void test_calculate_paird_node(struct mTree * tree) {
  off_t objects [MTREE_BFACTOR+1];
  off_t children[MTREE_BFACTOR+1];
  double paird[MTREE_BFACTOR+1][MTREE_BFACTOR+1];
  double radii[MTREE_BFACTOR+1];
  struct mTreeNode * node_ptr;
  off_t object;
  struct xyz_int * object_ptr;
  off_t child;
  struct mTreeNode * child_ptr;
  int i;

  off_t node;

  node = store_new (tree->nStore);
  node_ptr = store_read(tree->nStore, node);
  node_ptr->isLeaf = 0;

  for(i = 0; i < MTREE_BFACTOR; i++) {
    struct xyz_int * this_object_ptr;
    struct mTreeNode * this_child_ptr;

    node_ptr->children[i] = store_new(tree->nStore);

    this_child_ptr = store_read(tree->nStore, node_ptr->children[i]);
    this_child_ptr->dbObject = store_new(tree->oStore);

    this_child_ptr->radius = (double) i;

    this_object_ptr = store_read(tree->oStore, this_child_ptr->dbObject);

    this_object_ptr->x = i;
    this_object_ptr->y = 0;
    this_object_ptr->z = 0;
    //    this_object_ptr->i = i;

    store_write(tree->oStore, this_child_ptr->dbObject);
    store_release(tree->oStore, this_child_ptr->dbObject);
    store_write(tree->nStore, node_ptr->children[i]);
    store_release(tree->nStore, node_ptr->children[i]);

  }

  store_write(tree->nStore, node);

  child = store_new(tree->nStore);
  child_ptr = store_read(tree->nStore, child);
  object = store_new(tree->oStore);
  child_ptr->dbObject = object;
  child_ptr->radius = MTREE_BFACTOR;

  object_ptr = store_read(tree->oStore, object);
  object_ptr->x = MTREE_BFACTOR;
  object_ptr->y = 0;
  object_ptr->z = 0;
  //  object_ptr->i = MTREE_BFACTOR;

  store_write(tree->oStore, object);
  store_write(tree->nStore, child);
  calculate_paird(objects, children, paird, radii, tree, node_ptr, child, child_ptr);

  for(i = 0; i < MTREE_BFACTOR+1; i++) {
    int j;

    assert(radii[i] == i);

    if(i < MTREE_BFACTOR) {

      assert(children[i] == node_ptr->children[i]);

    } else {

      assert(children[i] == child);
    }

    for(j = 0; j < MTREE_BFACTOR+1; j++) {
      if(i < j) {
	assert(((int)paird[i][j]) == (j-i));
      } else {
	assert(((int)paird[i][j]) == (i-j));
      }
    }
  }


  store_release(tree->nStore, child);
  store_release(tree->oStore, object);
  store_release(tree->nStore, node);

  printf("PASS: calculate_paird (node)\n");



}

void test_choosePromotions(struct mTree * tree) {
  printf("SKIP: choose_promotions\n");
}
void test_initializePromotedNodes(struct mTree * tree) {
  printf("SKIP: initialize_promoted_nodes\n");
}

void test_chooseInsertNode(struct mTree * tree) {
  printf("SKIP: chooseInsertNode\n");
}

  /*
  off_t n1 = store_new(tree->nStore);
  off_t n2 = store_new(tree->nStore);
  
  struct mTreeNode * n1_ptr = store_read(tree->nStore, n1);
  struct mTreeNode * n2_ptr = store_read(tree->nStore, n2);

  store_release(tree->nStore, n1);
  store_release(tree->nStore, n2);

  */
